//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "die.h"

Define_Module(Die);

Die::Die()
{
    endServiceMsg.resize(4);
    endServiceMsg1.resize(4);
    endServiceMsg2.resize(4);
    endServiceMsg3.resize(4);

    msgServiced.resize(4);
    msgServiced1.resize(4);
    msgServiced2.resize(4);
    msgServiced3.resize(4);

    // 初始化
    for (int i = 0; i < 4; i++)
    {
        endServiceMsg[i] = nullptr;
        endServiceMsg1[i] = nullptr;
        endServiceMsg2[i] = nullptr;
        endServiceMsg3[i] = nullptr;
        msgServiced[i] = nullptr;
        msgServiced1[i] = nullptr;
        msgServiced2[i] = nullptr;
        msgServiced3[i] = nullptr;
    }

}

Die::~Die()
{
    for (int i = 0; i < 4; i++)
    {
        delete msgServiced[i];
        cancelAndDelete(endServiceMsg[i]);
        delete msgServiced1[i];
        cancelAndDelete(endServiceMsg1[i]);
        delete msgServiced2[i];
        cancelAndDelete(endServiceMsg2[i]);
        delete msgServiced3[i];
        cancelAndDelete(endServiceMsg3[i]);
    }

}

void Die::initialize()
{
    for (int i = 0; i < 4; i++)
    {
        endServiceMsg[i] = new cMessage("end-service");
        endServiceMsg1[i] = new cMessage("end-service");
        endServiceMsg2[i] = new cMessage("end-service");
        endServiceMsg3[i] = new cMessage("end-service");
    }

    // 初始化统计信号
    percentageSingal = registerSignal("percentage_reg");
    // 初始化队列长度和统计数据的队列
    every_queue.resize(4);
    for (int i = 0; i < 4; i++)
    {
        every_queue[i].resize(4);
    }
    every_queue_percent.resize(4);

    // 导入处理时延
    DELAY = par("die_delay").doubleValue();

    sum = 0;
}

int Die::routeMessage(Packet * packet_tobesent)
{
    // 首先获取计算路由的相关数据
    int hub_id_x = par("x").intValue();
    int hub_id_y = par("y").intValue();
    int dest_address_x = packet_tobesent->getDst_x();
    int dest_address_y = packet_tobesent->getDst_y();
    int src_address_x = packet_tobesent->getSrc_x();
    int src_address_y = packet_tobesent->getSrc_y();

    // 路由思路：
    // 对于Adie，其作用为：
    //    1. 若Adie所接收的packet是从oA来的，则转发至Xdie
    //    2. 若Adie所接收的packet是从Xdie来的，则转发至oA
    // 对于Xdie，其作用为：
    //    1. 计算目的节点的“坐标转换值”（以Xdie为核心的非规则mesh网络）
    //    2. 根据“坐标转换值”进行路由
    //    3. 当“坐标转换值”的差值为0时，表示已到达边缘Xdie，此时向Adie进行转发

    const char* self_name = getFullName();

    // 对hop加一，并设置到packet中
    int hop_plus = packet_tobesent->getHopcnt() + 1;
    packet_tobesent->setHopcnt(hop_plus);

    // 对于Adie，其作用为：
    if (self_name[0] == 'A')
    {
        // 1. 若Adie所接收的packet是从oA来的，则转发至Xdie
        const char* pervious = packet_tobesent->getSenderGate()->getOwnerModule()->getFullName();
        if (pervious[0] == 'o')
        {
            int oA_x = packet_tobesent->getSenderGate()->getOwnerModule()->par("x");
            int oA_y = packet_tobesent->getSenderGate()->getOwnerModule()->par("y");

            if ((oA_x == 0) and (oA_y >= 2) and (oA_y <= 15))
            {
                if ((packet_tobesent->getDst_x() == par("x").intValue()) and ((packet_tobesent->getDst_y() == par("y").intValue()))) // 考虑自发自收包的情况
                {
                    // send(packet_tobesent, "west$o");
                    return WEST;
                }
                else
                {
                    // send(packet_tobesent, "east$o");
                    return EAST;
                }
            }
            else if ((oA_x == 17) and (oA_y >= 2) and (oA_y <= 15))
            {
                if ((packet_tobesent->getDst_x() == par("x").intValue()) and ((packet_tobesent->getDst_y() == par("y").intValue()))) // 考虑自发自收包的情况
                {
                    //send(packet_tobesent, "east$o");
                    return EAST;
                }
                else
                {
                    //send(packet_tobesent, "west$o");
                    return WEST;
                }
            }
            else if ((oA_y == 0) and (oA_x >= 2) and (oA_x <= 15))
            {
                if ((packet_tobesent->getDst_x() == par("x").intValue()) and ((packet_tobesent->getDst_y() == par("y").intValue()))) // 考虑自发自收包的情况
                {
                    // send(packet_tobesent, "north$o");
                    return NORTH;
                }
                else
                {
                    //send(packet_tobesent, "south$o");
                    return SOUTH;
                }
            }
            else if ((oA_y == 17) and (oA_x >= 2) and (oA_x <= 15))
            {
                if ((packet_tobesent->getDst_x() == par("x").intValue()) and ((packet_tobesent->getDst_y() == par("y").intValue())))// 考虑自发自收包的情况
                {
                    //send(packet_tobesent, "south$o");
                    return SOUTH;
                }
                else
                {
                    //send(packet_tobesent, "north$o");
                    return NORTH;
                }
            }

        }
        // 2. 若Adie所接收的packet是从Xdie来的，则转发至oA
        else if (pervious[0] == 'X')
        {
            // 这里的oA_x、oA_y相当于X_x、X_y
            int oA_x = packet_tobesent->getSenderGate()->getOwnerModule()->par("x");
            int oA_y = packet_tobesent->getSenderGate()->getOwnerModule()->par("y");

            if ((oA_x == 1) and (oA_y >= 2) and (oA_y <= 15))
            {
                // send(packet_tobesent, "west$o");
                return WEST;
            }
            else if ((oA_x == 16) and (oA_y >= 2) and (oA_y <= 15))
            {
                // send(packet_tobesent, "east$o");
                return EAST;
            }
            else if ((oA_y == 1) and (oA_x >= 2) and (oA_x <= 15))
            {
                // send(packet_tobesent, "north$o");
                return NORTH;
            }
            else if ((oA_y == 16) and (oA_x >= 2) and (oA_x <= 15))
            {
                // send(packet_tobesent, "south$o");
                return SOUTH;
            }
        }
    }
    // 对于Xdie，其作用为：
    else if ((self_name[0] == 'X'))
    {
        // 1. 计算目的节点的“坐标转换值”（以Xdie为核心的非规则mesh网络）
        int new_dst_x = -1;
        int new_dst_y = -1;
        if ((dest_address_x == 0) and (dest_address_y >= 2) and (dest_address_y <= 15))
        {
            new_dst_x = 1;
            new_dst_y = dest_address_y;
        }
        else if ((dest_address_x == 17) and (dest_address_y >= 2) and (dest_address_y <= 15))
        {
            new_dst_x = 16;
            new_dst_y = dest_address_y;
        }
        else if ((dest_address_y == 0) and (dest_address_x >= 2) and (dest_address_x <= 15))
        {
            new_dst_x = dest_address_x;
            new_dst_y = 1;
        }
        else if ((dest_address_y == 17) and (dest_address_x >= 2) and (dest_address_x <= 15))
        {
            new_dst_x = dest_address_x;
            new_dst_y = 16;
        }

        // 2. 根据“坐标转换值”进行路由
        int x_offset = new_dst_x - hub_id_x;
        int y_offset = new_dst_y - hub_id_y;
        if (x_offset < 0)
        {
            if ((hub_id_x == 2) and (hub_id_y == 1))
            {
                // send(packet_tobesent, "south$o");
                return SOUTH;
            }
            else if ((hub_id_x == 2) and (hub_id_y == 16))
            {
                // send(packet_tobesent, "north$o");
                return NORTH;
            }
            else
            {
                //send(packet_tobesent, "west$o");
                return WEST;
            }
        }
        else if (x_offset > 0)
        {
            if ((hub_id_x == 15) and (hub_id_y == 1))
            {
                // send(packet_tobesent, "south$o");
                return SOUTH;
            }
            else if ((hub_id_x == 15) and (hub_id_y == 16))
            {
                // send(packet_tobesent, "north$o");
                return NORTH;
            }
            else
            {
                // send(packet_tobesent, "east$o");
                return EAST;
            }
        }
        else if ((x_offset == 0) and (y_offset < 0))
        {
            // send(packet_tobesent, "north$o");
            return NORTH;
        }
        else if ((x_offset == 0) and (y_offset > 0))
        {
            //send(packet_tobesent, "south$o");
            return SOUTH;
        }
        // 3. 当“坐标转换值”的差值为0时，表示已到达边缘Xdie，此时向Adie进行转发
        else if ((x_offset == 0) and (y_offset == 0))
        {
            // 这里的oA_x、oA_y相当于边缘Xdie的坐标，懒得改了
            int oA_x = par("x").intValue();
            int oA_y = par("y").intValue();
            if ((oA_x == 1) and (oA_y >= 2) and (oA_y <= 15))
            {
                //send(packet_tobesent, "west$o");
                return WEST;
            }
            else if ((oA_x == 16) and (oA_y >= 2) and (oA_y <= 15))
            {
                // send(packet_tobesent, "east$o");
                return EAST;
            }
            else if ((oA_y == 1) and (oA_x >= 2) and (oA_x <= 15))
            {
                //send(packet_tobesent, "north$o");
                return NORTH;
            }
            else if ((oA_y == 16) and (oA_x >= 2) and (oA_x <= 15))
            {
                //send(packet_tobesent, "south$o");
                return SOUTH;
            }
        }
    }
}

void Die::handleMessage(cMessage *msg)
{
    // 如果 结束服务了
    //   如果 某方向上的队列为空，则将该方向上的指针 msgServiced[0] 置为空
    //   否则 该方向上的指针 msgServiced[0] 指向刚出队的元素，进行发送，DELAY时间之后发送endServiceMsg
    // 另如果 该方向上的指针 msgServiced[0] 为空
    //   该方向上的指针 msgServiced[0] 指向 收到的msg，DELAY时间之后发送endServiceMsg
    // 否则 在该方向队列不满的情况下，将msg加入队列

    // self-message情况0
    if (msg->isSelfMessage())
    {
        for (int i = 0; i < 4; i++)
        {
            if (msg == endServiceMsg[i]){
                if (i == EAST)
                {
                    send(msgServiced[i], "east$o");
                }
                else if (i == WEST)
                {
                    send(msgServiced[i], "west$o");
                }
                else if (i == NORTH)
                {
                    send(msgServiced[i], "north$o");
                }
                else if (i == SOUTH)
                {
                    send(msgServiced[i], "south$o");
                }

                if (every_queue[NORTH][i].isEmpty()) {
                    msgServiced[i] = nullptr;
                }
                else
                {
                    msgServiced[i] = check_and_cast<Packet *>(every_queue[NORTH][i].pop());
                    scheduleAt(simTime()+(8 * msgServiced[i]->getField()) * DELAY, endServiceMsg[i]);
                }
                break;
            }
            // self-message情况1
            else if (msg == endServiceMsg1[i]){
                if (i == EAST)
                {
                    send(msgServiced1[i], "east$o");
                }
                else if (i == WEST)
                {
                    send(msgServiced1[i], "west$o");
                }
                else if (i == NORTH)
                {
                    send(msgServiced1[i], "north$o");
                }
                else if (i == SOUTH)
                {
                    send(msgServiced1[i], "south$o");
                }
                if (every_queue[SOUTH][i].isEmpty()) {
                    msgServiced1[i] = nullptr;
                }
                else
                {
                    msgServiced1[i] = check_and_cast<Packet *>(every_queue[SOUTH][i].pop());
                    scheduleAt(simTime()+(8 * msgServiced1[i]->getField()) * DELAY, endServiceMsg1[i]);
                }
                break;
            }
            // self-message情况2
            else if (msg == endServiceMsg2[i]){
                if (i == EAST)
                {
                    send(msgServiced2[i], "east$o");
                }
                else if (i == WEST)
                {
                    send(msgServiced2[i], "west$o");
                }
                else if (i == NORTH)
                {
                    send(msgServiced2[i], "north$o");
                }
                else if (i == SOUTH)
                {
                    send(msgServiced2[i], "south$o");
                }
                if (every_queue[WEST][i].isEmpty()) {
                    msgServiced2[i] = nullptr;
                }
                else
                {
                    msgServiced2[i] = check_and_cast<Packet *>(every_queue[WEST][i].pop());
                    scheduleAt(simTime()+(8 * msgServiced2[i]->getField()) * DELAY, endServiceMsg2[i]);
                }
                break;
            }
            // self-message情况3
            else if (msg == endServiceMsg3[i]){
                if (i == EAST)
                {
                    send(msgServiced3[i], "east$o");
                }
                else if (i == WEST)
                {
                    send(msgServiced3[i], "west$o");
                }
                else if (i == NORTH)
                {
                    send(msgServiced3[i], "north$o");
                }
                else if (i == SOUTH)
                {
                    send(msgServiced3[i], "south$o");
                }
                if (every_queue[EAST][i].isEmpty()) {
                    msgServiced3[i] = nullptr;
                }
                else
                {
                    msgServiced3[i] = check_and_cast<Packet *>(every_queue[EAST][i].pop());
                    scheduleAt(simTime()+(8 * msgServiced3[i]->getField()) * DELAY, endServiceMsg3[i]);
                }
                break;
            }
        }
    }

    // packet情况
    else
    {
        // 收到packet首先判断是从哪个口进来的
        const char * name = msg->getArrivalGate()->getName();
        EV << "msg->getArrivalGate()->getName()" << msg->getArrivalGate()->getName() << endl;

        // 获取要发送的端口
        int dst_port = routeMessage(check_and_cast<Packet *>(msg));
        EV << "dst_port is " << dst_port << endl;

        // 若从n口进入
        if (name[0] == 'n')
        {
            // 若n口没被服务，则直接服务
            if (!msgServiced[dst_port]){
                msgServiced[dst_port] = check_and_cast<Packet *>(msg);
                scheduleAt(simTime()+(8 * msgServiced[dst_port]->getField()) * DELAY, endServiceMsg[dst_port]);
            }
            // 若n口正在被服务，则入队
            else
            {
                if (every_queue[NORTH][dst_port].getLength() < par("queue_len").intValue())
                {
                    every_queue[NORTH][dst_port].insert(msg);
                }
                else
                {
                    bubble("chaoguole");
                    delete msg;
                }
            }
        }
        // 若从s口进入
        if (name[0] == 's')
        {
            // 若s口没被服务，则直接服务
            if (!msgServiced1[dst_port]){
                msgServiced1[dst_port] = check_and_cast<Packet *>(msg);
                scheduleAt(simTime()+(8 * msgServiced1[dst_port]->getField()) * DELAY, endServiceMsg1[dst_port]);
            }
            // 若s口正在被服务，则入队
            else
            {
                if (every_queue[SOUTH][dst_port].getLength() < par("queue_len").intValue())
                {
                    every_queue[SOUTH][dst_port].insert(msg);
                }
                else
                {
                    bubble("chaoguole");
                    delete msg;
                }
            }
        }
        // 若从w口进入
        if (name[0] == 'w')
        {
            // 若w口没被服务，则直接服务
            if (!msgServiced2[dst_port]){
                msgServiced2[dst_port] = check_and_cast<Packet *>(msg);
                scheduleAt(simTime()+(8 * msgServiced2[dst_port]->getField()) * DELAY, endServiceMsg2[dst_port]);
            }
            // 若w口正在被服务，则入队
            else
            {
                if (every_queue[WEST][dst_port].getLength() < par("queue_len").intValue())
                {
                    every_queue[WEST][dst_port].insert(msg);
                }
                else
                {
                    bubble("chaoguole");
                    delete msg;
                }
            }
        }
        // 若从e口进入
        if (name[0] == 'e')
        {
            // 若e口没被服务，则直接服务
            if (!msgServiced3[dst_port]){
                msgServiced3[dst_port] = check_and_cast<Packet *>(msg);
                scheduleAt(simTime()+(8 * msgServiced3[dst_port]->getField()) * DELAY, endServiceMsg3[dst_port]);
            }
            // 若e口正在被服务，则入队
            else
            {
                if (every_queue[EAST][dst_port].getLength() < par("queue_len").intValue())
                {
                    every_queue[EAST][dst_port].insert(msg);
                }
                else
                {
                    bubble("chaoguole");
                    delete msg;
                }
            }
        }

    }


    sum = 0;
    // 判断A和X的队列数
    int queue_number_a_x;
    /*if ((par("x").intValue() >= 1) and (par("x").intValue() <= 16) and (par("y").intValue() <= 16) and (par("y").intValue() >= 1))
    {
        EV << "here is xdie " << endl;
        queue_number_a_x = 4;
    }
    else
    {
        EV << "here is adie " << endl;
        queue_number_a_x = 2;
    }

    // 统计某个die中的所有队列中的利用率
    for (int i = 0; i < queue_number_a_x; i++)
    {
        every_queue_percent[i] = (double)every_queue[i].getLength() / (double)par("queue_len").intValue();
        EV << "every_queue_percent " << i << "is " << every_queue_percent[i] << endl;
        sum = sum + every_queue_percent[i];
        EV << "sum is " << sum << endl;
    }*/

    // 统计die的总体利用率
    double percent = (double)sum / queue_number_a_x;

    //emit(percentageSingal, percent);
}

void Die::refreshDisplay() const
{
    char buf[100];
    double percent = (double)sum / 4;
    sprintf(buf, "percentage:\n %f", percent);
    getDisplayString().setTagArg("t", 0, buf);
}


